package asteroids.model.programs.statements.ProgramStatements;

import asteroids.model.programs.Program;
import asteroids.model.programs.expressions.Expression;
import asteroids.model.programs.expressions.BasicExpressions.BooleanLiteral;
import asteroids.model.programs.statements.ProgramStatement;
import asteroids.model.programs.statements.Statement;
import asteroids.model.programs.types.Type;

/**
 * A class representing an If-statement. This class is a sub-class of the
 * Statemant-class.
 * 
 * An if-statement has a condition that can be evaluated to a boolean value.
 * When this condition evaluates to true, the 'then'-part of the if-statement
 * will be executed.
 * When this condition evaluates to false, the 'else'-part (called otherwise) in
 * this implementation) will be executed
 * 
 * @author Michiel De Cuyper
 * @version 08.05
 * 
 */
public class IfStatement extends ProgramStatement {

	/**
	 * Constructor for the IfStatement-class.
	 * 
	 * @param line
	 *            The line on which this if-statement is found
	 *            Gets passed to the Statement-class
	 * @param column
	 *            The column in which this if-statement is found
	 *            Gets passed to the Statement-class
	 * @param condition
	 *            A boolean expression that represents the condition for this
	 *            if-statement
	 * @param then
	 *            A statement that will be executed when the condition evaluates
	 *            to true
	 * @param otherwise
	 *            A statement that will be executed when the condition evaluates
	 *            to false
	 */
	public IfStatement(int line, int column, Expression condition, Statement then, Statement otherwise) {
		super(line, column);
		this.setCondition(condition);
		this.setThen(then);
		this.setOtherwise(otherwise);
	}

	/**
	 * Executes this IfStatement, by evaluating the condition. If the condition
	 * evaluates to true, the then-part is executed. If the condition evaluates
	 * to false, the else-part is executed
	 * 
	 * @effect ...
	 *         | if(getCondition().getValue() == true) getThen().execute()
	 *         ...
	 *         | if(getCondition().getValue() == false) getOtherwise().execute()
	 */
	@Override
	public void execute() {
		if (((BooleanLiteral) this.getCondition().getResult()).getValue()) {
			this.getThen().execute();
		}
		else {
			this.getOtherwise().execute();
		}
	}
	
	public void setProgram(Program program) {
		super.setProgram(program);
		this.getThen().setProgram(program);
		this.getOtherwise().setProgram(program);
	}

	/**
	 * Sets the condition of this if-statement to a given condition.
	 * 
	 * @param condition
	 *            The condition to be set
	 * @post ...
	 *       | (new this).getCondition == condition
	 */
	public void setCondition(Expression condition) {
		this.condition = condition;
	}

	/**
	 * Returns the condition of this if-statement
	 * 
	 * @return ...
	 *         | return this.condition;
	 */
	public Expression getCondition() {
		return this.condition;
	}

	/**
	 * An Expression-variable, storing the condition of this if-statement.
	 */
	private Expression condition;

	/**
	 * Sets the then-statement of this if-statement to a given statement.
	 * 
	 * @param then
	 *            The then-statement to be set
	 * @post ...
	 *       | (new this).getThen == then
	 */
	public void setThen(Statement then) {
		this.then = then;
	}

	/**
	 * Returns the then-statement of this if-statement
	 * 
	 * @return ...
	 *         | return this.then;
	 */
	public Statement getThen() {
		return this.then;
	}

	/**
	 * A statement-variable, storing the then-part of this if-statement.
	 * This statement will be executed when the condition evaluates to true.
	 */
	private Statement then;

	/**
	 * Sets the else-statement of this if-statement to a given statement.
	 * 
	 * @param condition
	 *            The condition to be set
	 * @post ...
	 *       | (new this).getCondition == condition
	 */
	public void setOtherwise(Statement otherwise) {
		this.otherwise = otherwise;
	}

	/**
	 * Returns the else-statement of this if-statement
	 * 
	 * @return ...
	 *         | return this.otherwise
	 */
	public Statement getOtherwise() {
		return this.otherwise;
	}

	/**
	 * A statement-variable, storing the else-part of this if-statement.
	 * This statement will be executed when the condition evaluates to false.
	 */
	private Statement otherwise;

	@Override
	public Expression getExpression() {
		return condition;
	}

	@Override
	public String typeCheckStatement(String errorMessage) {
		
		String conditionMessage = null;
		if(getExpression().getType() != Type.BOOLEAN){
			conditionMessage = "If condition at line " + getLine() + "and column " +
								getColumn() + " is of type " + getExpression().getType() +
								" and should be of type " + Type.BOOLEAN + "\n";
		}
		
		String newErrorMessage = getExpression().typeCheckExpression(errorMessage);
		String thenErrorMessage = getThen().typeCheckStatement(newErrorMessage);
		String otherwiseErrorMessage = getOtherwise().typeCheckStatement(thenErrorMessage);
		
		
		if(otherwiseErrorMessage != null && conditionMessage != null) otherwiseErrorMessage += conditionMessage;
		if(otherwiseErrorMessage == null) otherwiseErrorMessage = conditionMessage;
		return otherwiseErrorMessage;
	}

}
